package jmine.tec.utils.io;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;

/**
 * Iterável sobre arquivos em um patch que estão conforme um filtro.
 * 
 * @author lundberg
 */
public class FileWalker implements Iterable<File> {

    private final FileFilter filter;

    private final File basePath;

    /**
     * Contrutor
     * 
     * @param basePath basePath
     * @param regex regex
     */
    public FileWalker(File basePath, final String regex) {
        this(basePath, new FileFilter() {
            private final Pattern pattern = Pattern.compile(regex);

            public boolean accept(File pathname) {
                try {
                    return this.pattern.matcher(pathname.getCanonicalPath()).matches();
                } catch (IOException e) {
                    return false;
                }
            }
        });
    }

    /**
     * Construtor
     * 
     * @param basePath basePath
     * @param filter filter
     */
    public FileWalker(File basePath, FileFilter filter) {
        this.basePath = basePath;
        this.filter = filter;
    }

    /**
     * {@inheritDoc}
     */
    public Iterator<File> iterator() {
        return new FileIterator(this.basePath);
    }

    /**
     * Iterator sobre arquivos.
     * 
     * @author lundberg
     */
    class FileIterator implements Iterator<File> {

        private Iterator<Iterator<File>> it;

        private File baseFile;

        private Iterator<File> current;

        private Boolean hasNext;

        /**
         * Construtor
         * 
         * @param baseFile baseFile
         */
        protected FileIterator(File baseFile) {
            this.baseFile = baseFile;
        }

        /**
         * Busca o próximo arquivo
         */
        private void findNext() {
            if (this.baseFile != null && this.baseFile.isDirectory()) {
                if (this.it == null) {
                    List<Iterator<File>> sub = new ArrayList<Iterator<File>>();
                    for (File file : this.baseFile.listFiles()) {
                        if (!file.getName().startsWith(".")) {
                            sub.add(new FileIterator(file));
                        }
                    }
                    this.it = sub.iterator();
                    if (this.it.hasNext()) {
                        this.current = this.it.next();
                    }
                }
                while (this.current != null && !this.current.hasNext()) {
                    if (this.it.hasNext()) {
                        this.current = this.it.next();
                    } else {
                        this.current = null;
                    }
                }
            }
        }

        /**
         * {@inheritDoc}
         */
        public boolean hasNext() {
            if (this.baseFile == null) {
                return false;
            } else if (this.baseFile.isDirectory()) {
                if (this.hasNext == null) {
                    this.findNext();
                }
                return this.current != null;
            } else {
                return FileWalker.this.filter.accept(this.baseFile);
            }
        }

        /**
         * {@inheritDoc}
         */
        public File next() {
            if (!this.hasNext()) {
                throw new IllegalStateException();
            }
            if (this.baseFile.isDirectory()) {
                File file = this.current.next();
                this.findNext();
                return file;
            } else {
                File next = this.baseFile;
                this.baseFile = null;
                return next;
            }
        }

        /**
         * {@inheritDoc}
         */
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }
}
